package cn.udrm.water.base.v1.manager.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.udrm.water.base.v1.commons.models.PageModel;
import cn.udrm.water.base.v1.commons.models.ZTreeNodeVo;
import cn.udrm.water.base.v1.manager.cache.BaseDepartmentCache;
import cn.udrm.water.base.v1.manager.dto.BaseDepartmentDto;
import cn.udrm.water.base.v1.manager.dto.BaseDepartmentDtoMapper;
import cn.udrm.water.base.v1.manager.dto.BasePersonDto;
import cn.udrm.water.base.v1.manager.dto.BasePersonDtoMapper;
import cn.udrm.water.base.v1.manager.dto.BaseRoleDto;
import cn.udrm.water.base.v1.manager.dto.BaseRoleDtoMapper;
import cn.udrm.water.base.v1.manager.entities.BaseDepartment;
import cn.udrm.water.base.v1.manager.entities.BaseOrganization;
import cn.udrm.water.base.v1.manager.entities.BasePerson;
import cn.udrm.water.base.v1.manager.entities.BaseRole;
import cn.udrm.water.base.v1.manager.repository.BaseDepartmentRepository;
import cn.udrm.water.base.v1.manager.repository.BaseOrganizationRepository;

@Service
@Transactional(readOnly=true)
@CacheConfig(cacheNames="base_departments")
public class BaseDepartmentService {
	@Autowired
	private BaseDepartmentRepository departmentRepository;
	@Autowired
	private BaseDepartmentCache baseDepartmentCache;
	@Autowired
	private BaseDepartmentDtoMapper baseDepartmentDtoMapper;
	@Autowired
	private BaseOrganizationRepository organizationRepository;
	@Autowired
	private BasePersonDtoMapper personDtoMapper;
	@Autowired
	private BaseRoleDtoMapper roleDtoMapper;
	
	@Cacheable(key="methodName+#p0")
	public BaseDepartmentDto getWithAllById(String id) {
		BaseDepartment withPersons = departmentRepository.getWithPersons(id);
		BaseDepartment withRoles = departmentRepository.getWithRoles(id);
		if (withPersons != null) {
			BaseDepartmentDto from = baseDepartmentDtoMapper.from(withPersons);
			List<BasePerson> persons = withPersons.getPersons();
			if (persons != null) {
				List<BasePersonDto> personsDto = personDtoMapper.from(persons);
				from.setPersonDtos(personsDto);
			}
			List<BaseRole> roles = withRoles.getRoles();
			if (roles != null) {
				List<BaseRoleDto> rolesDto = roleDtoMapper.from(roles);
				from.setRoleDtos(rolesDto);
			}
			return from;
		}
		return null;
	}
	
	@Cacheable(key="methodName+#p0")
	public List<BaseDepartmentDto> findByOrganizationId(String organizationId) {
		List<BaseDepartment> findByOrganizationId = departmentRepository.findByOrganizationId(organizationId);
		if (findByOrganizationId == null) {
			return null;
		}
		List<BaseDepartmentDto> list = new ArrayList<BaseDepartmentDto>();
		for (BaseDepartment baseDepartment : findByOrganizationId) {
			List<BaseDepartment> childrens = baseDepartment.getChildrens();
			BaseDepartmentDto from = baseDepartmentDtoMapper.from(baseDepartment);
			if (childrens != null && childrens.size() != 0) {
				List<BaseDepartmentDto> from2 = baseDepartmentDtoMapper.from(childrens);
				from.setChildrensDto(from2);
			}
			list.add(from);
		}
		return list;
	}
	
	/**
	 * 获取管理单位树
	 * @return
	 */
	@Cacheable(key="methodName+#p0")
	public List<ZTreeNodeVo> getDepartmentZtree(String parentId) {
		List<BaseDepartment> list = departmentRepository.findByParentId(parentId);
		List<BaseDepartmentDto> from = baseDepartmentDtoMapper.from(list);
		List<ZTreeNodeVo> treeNodeVos = new ArrayList<ZTreeNodeVo>(from.size());
		from.forEach((managerUnitDto) -> {
			ZTreeNodeVo zTreeNodeVo = new ZTreeNodeVo(managerUnitDto.getId(), managerUnitDto.getName(), managerUnitDto.getParentGuid());
			treeNodeVos.add(zTreeNodeVo);
		});
		return treeNodeVos;
	}

	/**
	 * 分页条件查询
	 * @param pageNumberStr
	 * @param pageSizeStr
	 * @param waterGateSearch
	 * @param sortField
	 * @param sortType
	 * @return
	 */
	public PageModel<BaseDepartmentDto> getPageByConditions(String pageNumberStr, String pageSizeStr, final BaseDepartmentDto departmentSearch
			, String sortField, String sortType) {
		int number = 0,
			size = 0;
		try {
			number = Integer.parseInt(pageNumberStr);
		} catch (Exception e) {}
		try {
			size = Integer.parseInt(pageSizeStr);
		} catch (Exception e) {}
		size = size == 0 ? 10 : size;
		Sort sort = null;
		if (!StringUtils.isEmpty(sortField)) {
			try {
				sort = new Sort(StringUtils.isEmpty(sortType) ? Direction.ASC : (sortType.equals("ASC")) ? Direction.ASC : Direction.DESC, sortField);
			} catch (Exception e) {}
		} else {
			sort = new Sort(Direction.ASC, "createDate");
		}
		Pageable pageable = PageRequest.of(number, size, sort);
		Specification<BaseDepartment> specification = new Specification<BaseDepartment>() {
			private static final long serialVersionUID = 1L;

			@Override
			public Predicate toPredicate(Root<BaseDepartment> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> predicates = new ArrayList<>();
				
				if (predicates.size() == 0) {
					return null;
				}
				Predicate predicateArr[] = new Predicate[predicates.size()];
				return cb.and(predicates.toArray(predicateArr));
			}
		};
		Page<BaseDepartment> page = departmentRepository.findAll(specification, pageable);
		List<BaseDepartment> content = page.getContent();
		List<BaseDepartmentDto> dtos = baseDepartmentDtoMapper.from(content);
		PageModel<BaseDepartmentDto> pageModel = new PageModel<BaseDepartmentDto>(page.getSize(), page.getNumber(), page.getTotalElements(), dtos);
		return pageModel;
	}
	
	@Cacheable(key="methodName+#p0")
	public BaseDepartmentDto getWithRoles(String id) {
		BaseDepartment withRoles = departmentRepository.getWithRoles(id);
		if (withRoles != null) {
			List<BaseRole> roles = withRoles.getRoles();
			BaseDepartmentDto from = baseDepartmentDtoMapper.from(withRoles);
			if (roles != null) {
				List<BaseRoleDto> rolesDto = roleDtoMapper.from(roles);
				from.setRoleDtos(rolesDto);
			}
			return from;
		}
		return null;
	}
	
	@Cacheable(key="methodName+#p0")
	public BaseDepartmentDto getWithPersons(String id) {
		BaseDepartment baseDepartment = departmentRepository.getWithPersons(id);
		if (baseDepartment != null) {
			List<BasePerson> persons = baseDepartment.getPersons();
			BaseDepartmentDto from = baseDepartmentDtoMapper.from(baseDepartment);
			if (persons != null) {
				List<BasePersonDto> from2 = personDtoMapper.from(persons);
				from.setPersonDtos(from2);
			}
			return from;
		}
		return null;
	}
	
	@Cacheable(key="methodName+#p0")
	public BaseDepartmentDto getWithChildrens(String id) {
		BaseDepartment baseDepartment = departmentRepository.getWithChildrens(id);
		if (baseDepartment != null) {
			List<BaseDepartment> childrens = baseDepartment.getChildrens();
			BaseDepartmentDto from = baseDepartmentDtoMapper.from(baseDepartment);
			if (childrens != null) {
				List<BaseDepartmentDto> from2 = baseDepartmentDtoMapper.from(childrens);
				from.setChildrensDto(from2);
			}
			return from;
		}
		return null;
	}
	
	@Cacheable(key="methodName+#p0")
	public BaseDepartmentDto getSelfById(String id) {
		BaseDepartment department = departmentRepository.findSelfById(id);
		BaseDepartmentDto departmentDto = baseDepartmentDtoMapper.from(department);
		return departmentDto;
	}
	
	@Transactional(readOnly=false)
	public BaseDepartmentDto save(BaseDepartmentDto departmentDto) {
		BaseDepartment department = baseDepartmentDtoMapper.fromBaseDepartmentDto(departmentDto);
		department.setId(null);
		department.setCreateDate(new Date());
		/*
		 * 设置父对象
		 */
		BaseDepartment parent = null;
		String parentGuid = departmentDto.getParentGuid();
		if (StringUtils.isNotBlank(parentGuid)) {
			parent = departmentRepository.findSelfById(parentGuid);
		}
		if (parent != null && StringUtils.isNotBlank(parent.getId())) {
			department.setParent(parent);
		}
		/*
		 * 设置所属组织机构
		 */
		if (parent == null) {
			BaseOrganization baseOrganization = null;
			String organizationGuid = departmentDto.getOrganizationGuid();
			if (StringUtils.isNotBlank(organizationGuid)) {
				Optional<BaseOrganization> findById = organizationRepository.findById(organizationGuid);
				baseOrganization = findById.get();
			}
			if (baseOrganization != null) {
				department.setOrganization(baseOrganization);
			}
		}
		/*
		 * 设置所属角色
		 */
		List<BaseRole> roles = null;
		List<BaseRoleDto> roleDtos = departmentDto.getRoleDtos();
		if (roleDtos != null && roleDtos.size() != 0) {
			roles = new ArrayList<BaseRole>();
			for (BaseRoleDto baseRoleDto : roleDtos) {
				roles.add(new BaseRole(baseRoleDto.getId()));
			}
		}
		if (roles != null) {
			department.setRoles(roles);
		}
		department = baseDepartmentCache.saveAndFlush(department);
		BaseDepartmentDto dto = baseDepartmentDtoMapper.from(department);
		return dto;
	}
	@Transactional(readOnly=false)
	public BaseDepartmentDto update(BaseDepartmentDto departmentDto) {
		BaseDepartment department = baseDepartmentDtoMapper.fromBaseDepartmentDto(departmentDto);
		/*
		 * 设置父对象
		 */
		BaseDepartment parent = null;
		String parentGuid = departmentDto.getParentGuid();
		if (StringUtils.isNotBlank(parentGuid)) {
			parent = departmentRepository.findSelfById(parentGuid);
		}
		if (parent != null && StringUtils.isNotBlank(parent.getId())) {
			department.setParent(parent);
		}
		/*
		 * 设置所属组织机构
		 */
		if (parent == null) {
			BaseOrganization baseOrganization = null;
			String organizationGuid = departmentDto.getOrganizationGuid();
			if (StringUtils.isNotBlank(organizationGuid)) {
				Optional<BaseOrganization> findById = organizationRepository.findById(organizationGuid);
				if (findById != null && findById.isPresent()) {
					baseOrganization = findById.get();
				}
			}
			if (baseOrganization != null) {
				department.setOrganization(baseOrganization);
			}
		}
		/*
		 * 设置所属角色
		 */
		List<BaseRole> roles = null;
		List<BaseRoleDto> roleDtos = departmentDto.getRoleDtos();
		if (roleDtos != null && roleDtos.size() != 0) {
			roles = new ArrayList<BaseRole>();
			for (BaseRoleDto baseRoleDto : roleDtos) {
				roles.add(new BaseRole(baseRoleDto.getId()));
			}
		}
		if (roles != null) {
			department.setRoles(roles);
		}
		department = baseDepartmentCache.saveAndFlush(department);
		BaseDepartmentDto dto = baseDepartmentDtoMapper.from(department);
		return dto;
	}
	
	/**
	 * 删除所有信息, 包括其级联的所有实体
	 * @param ids
	 */
	@Transactional(readOnly=false)
	public void deleteAll(String[] ids) {
		if (ids == null || ids.length == 0) {
			return;
		}
		List<BaseDepartment> departments = new ArrayList<BaseDepartment>();
		for (String id : ids) {
			departments.add(new BaseDepartment(id));
		}
		this.deleteAll(departments);
	}
	
	/**
	 * @param iterable
	 */
	@Transactional(readOnly=false)
	private void deleteAll(Iterable<BaseDepartment> iterable) {
		if (iterable == null || iterable.iterator() == null) {
			return;
		}
		Iterator<BaseDepartment> iterator = iterable.iterator();
		while (iterator.hasNext()) {
			BaseDepartment department = iterator.next();
			department = departmentRepository.findSelfById(department.getId());
			List<BaseDepartment> childrens = department.getChildrens();
			this.deleteAll(childrens);
			baseDepartmentCache.delete(department);
		}
	}
	
	@Transactional(readOnly=false)
	public void deleteAllSelf(String[] ids) {
		if (ids == null || ids.length == 0) {
			return;
		}
		for (String id : ids) {
			baseDepartmentCache.deleteById(id);
		}
	}
}
